function typeOf(obj) {
  const toString = Object.prototype.toString
  const str = toString.call(obj)
  const regExp = /\[object (\w*)\]/
  if (regExp.test(str)) {
    return (RegExp.$1 || '').toLowerCase()
  }
}

function camelcaseToHyphen(str) {
  return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
}

function getParentComponent(context, componentName, findonce) {
  if (typeOf(findonce) !== 'boolean') {
    findonce = true
  }
  const ignoreVal = findonce ? null : []

  if (!context || !componentName) {
    return
  }
  var parent = context
  var result = []

  var componentNames = []

  if (typeOf(componentName) === 'string') {
    componentNames = [componentName]
  } else if (typeOf(componentName) === 'array') {
    componentNames = componentName
  }
  if (!componentNames.length) {
    return ignoreVal
  }
  for (var z in componentNames) {
    componentNames[z] = camelcaseToHyphen(componentNames[z])
  }
  while (parent.$parent) {
    parent = parent.$parent
    var name = getComponentName(parent)
    if (name && componentNames.indexOf(name) > -1) {
      result.push(parent)
      if (findonce) break
    }
  }
  return findonce ? result[0] || null : result
}

function getBrotherComponent(context, componentName, findonce) {
  if (typeOf(findonce) !== 'boolean') {
    findonce = true
  }
  const ignoreVal = findonce ? null : []

  if (!context || !componentName) return ignoreVal

  var componentNames = []

  if (typeOf(componentName) === 'string') {
    componentNames = [componentName]
  } else if (typeOf(componentName) === 'array') {
    componentNames = componentName
  }
  if (!componentNames.length) {
    return ignoreVal
  }
  for (var z in componentNames) {
    componentNames[z] = camelcaseToHyphen(componentNames[z])
  }
  const parent = context.$parent || {}
  const brothers = parent._isVue ? parent.$children : []
  const result = []

  for (var i in brothers) {
    const brother = brothers[i]
    var name = getComponentName(brother)
    if (name &&
          componentNames.indexOf(name) > -1 &&
          context._uid !== brother._uid) {
      result.push(brother)
      if (findonce) {
        break
      }
    }
  }
  return findonce ? result[0] || null : result
}

function getComponentName(context) {
  context = context || this
  var vnode = context.$vnode || {}
  var result = null
  var tag = String(vnode.tag || '')
  const regExp = /vue-component-\w*-(.*)$/
  if (regExp.test(tag)) {
    result = RegExp.$1
    result = camelcaseToHyphen(result)
  }
  return result
}

function getChildComponent(context, componentName, findonce) {
  if (typeOf(findonce) !== 'boolean') {
    findonce = true
  }
  const ignoreVal = findonce ? null : []
  if (!context || !componentName) {
    return ignoreVal
  }

  var componentNames = []

  if (typeOf(componentName) === 'string') {
    componentNames = [componentName]
  } else if (typeOf(componentName) === 'array') {
    componentNames = componentName
  }
  if (!componentNames.length) {
    return ignoreVal
  }
  for (var z in componentNames) {
    componentNames[z] = camelcaseToHyphen(componentNames[z])
  }
  const queryHandler = function(components) {
    var result = []
    for (let i = 0; i < components.length; i++) {
      const child = components[i]
      const name = getComponentName(child)

      if (name && componentNames.indexOf(name) > -1) {
        if (findonce) return [child]
        else {
          result.push(child)
        }
      }
      if (child.$children.length) {
        result = result.concat(queryHandler(child.$children))
      }
    }
    return result
  }

  var children = context.$children
  var results = queryHandler(children || [])
  return findonce ? results[0] || null : results
}

export default {
  install(Vue) {
    /**
     * @method：$getComponentName
     * @params：
     * @description：获取组件名称
     */
    Vue.prototype.$getComponentName = getComponentName

    /**
     * @method：$getParentComponent
     * @params：
     * componentName：组件名（单个）
     * componentNames：组件名（多个）
     * @description：获取父组件
     */
    Vue.prototype.$getParentComponent = function(componentName, componentNames) {
      return getParentComponent(this, componentName, componentNames)
    }

    /**
     * @method：getChildComponent
     * @params：
     * componentName：组件名
     * findonce：是否只返回一个实例（默认为是）
     * @description：获取子组件
     */

    Vue.prototype.$getChildComponent = function(componentName, findonce) {
      return getChildComponent(this, componentName, findonce)
    }

    /**
     * @method：$getBrotherComponent
     * @params：
     * componentName:组件名
     * findonce：是否只返回一个实例（默认为是）
     * @description：获取兄弟组件
     */
    Vue.prototype.$getBrotherComponent = function(componentName, findonce) {
      return getBrotherComponent(this, componentName, findonce)
    }
    /**
     * @method：$dispatch
     * @params：
     * componentName：组件名
     * eventName：事件名
     * params：参数
     * @description：与父级组件通信（冒泡消息）
     */
    Vue.prototype.$dispatch = function(componentName, eventName, ...params) {
      const parent = getParentComponent(this, componentName) || {}
      if (parent._isVue) {
        parent.$emit(...[eventName].concat([...params]))
      }
    }

    /**
     * @method：$broadcast
     * @params：
     * componentName：组件名
     * eventName：事件名
     * params：参数
     * @description：与子组件通信（广播消息）
     */
    Vue.prototype.$broadcast = function(componentName, eventName, ...params) {
      const children = getChildComponent(this, componentName, false) || []
      children.forEach((child) => {
        if (child._isVue) {
          child.$emit(...[eventName].concat([...params]))
        }
      })
    }

    /**
     * @method：$deliver
     * @params：
     * componentName：组件名
     * eventName：事件名
     * params：参数
     * @description：与兄弟组件通信
     */
    Vue.prototype.$deliver = function(componentName, eventName, ...params) {
      const brothers = getBrotherComponent(this, componentName, false) || []
      brothers.forEach((brother) => {
        if (brother._isVue) {
          brother.$emit(...[eventName].concat([...params]))
        }
      })
    }
  }
}
